<script setup>
import { ref, onMounted, onUnmounted } from 'vue';

// WebSocket 实例
let ws = null;

// webrpc配置
const rpcConfig = {
  iceServers: [], // 使用局域网环境时可能不需要 STUN/TURN
  iceTransportPolicy: 'all'
};

// 用户状态
const userName = ref('');
const clientList = ref([]);
const loggedIn = ref(false);
const chatMessages = ref([]);

// 声明连接的client
const client = ref('')

// 多个连接的RTCPeerConnection对象
const peerConnectionList = ref(new Map())
const dataChannelList = ref(new Map())

// 发送的文件
const file = ref(null)
const receivedFile = ref(null);


// MessageType 静态变量
const MessageType = {
  login: 0,
  signal: 1,
  text: 2,
  user: 3
};

// 连接到 WebSocket 服务端
const connectWebSocket = () => {
  ws = new WebSocket('ws://localhost:8080');

  ws.onopen = () => {
    console.log('WebSocket 连接成功');
    
  };

  ws.onmessage = (event) => {
    const message = JSON.parse(event.data);
    handleMessage(message);
  };

  ws.onclose = () => {
    console.log('WebSocket 连接关闭');
  };
};

// 处理接收到的消息
const handleMessage = (message) => {
  switch (message.msgtype) {
    case MessageType.text:
      console.log('收到文本消息:', message.data);
      break;
    case MessageType.user:
      clientList.value = message.data;
      console.log('更新的用户列表:', clientList.value);
      break;
    case MessageType.signal:
      handleSignalMessage(message);
      break;
    default:
      console.log('未知消息类型');
  }
};

// 处理信令消息
const handleSignalMessage = async (message) => {
  if (message.type === 'offer') {
    await handleOffer(message);
  } else if (message.type === 'answer') {
    await handleAnswer(message);
  } else if (message.type === 'candidate') {
    await handleCandidate(message);
  }
};

// 创建点对点连接
const createPeerConnection = () => {

  peerConnectionList.value.set(client.value, new RTCPeerConnection(rpcConfig));
  const peerConnection = peerConnectionList.value.get(client.value);

  // ICE 处理
  peerConnection.onicecandidate = (event) => {
    // todo 第一次连接，这里发送了五次
    console.log("ICE 处理", event.candidate);
    if (event.candidate) {
      ws.send(JSON.stringify({
        msgtype: MessageType.signal,
        client: client.value,
        type: 'candidate',
        data: event.candidate
      }));
    }
  };

  // 监听远端 DataChannel 创建事件
  peerConnection.ondatachannel = (event) => {
    const receivedChannel = event.channel;
    console.log(`收到新的 DataChannel: ${receivedChannel.label}`);

    // 为不同的 DataChannel 分别设置消息处理
    receivedChannel.onmessage = (event) => {
      console.log(`收到来自 ${receivedChannel.label} 通道的消息:`, event.data);
      if (receivedChannel.label === 'chat') {
        chatDataChannelOnmessage(event)
      } else if (receivedChannel.label === 'file') {
        fileDataChannelOnmessage(event)
      }
    };

    receivedChannel.onopen = () => {
      console.log(`${receivedChannel.label} DataChannel 已打开`);
    };

    receivedChannel.onclose = () => {
      console.log(`${receivedChannel.label} DataChannel 已关闭`);
    };
  };

  // todo 这里后期也可以自定义，客户端可以选择是否开启哪些channel
  createChatPeerConnection(peerConnection)
  createFilePeerConnection(peerConnection)
};

const chatDataChannelOnmessage = (event) => {
  console.log("chatDataChannelOnmessage", event.data);
  chatMessages.value.push({
    from: `对方(${client.value})`,
    data: event.data
  });
}

/* const fileDataChannelOnmessage = (event) => {
    console.log("fileDataChannelOnmessage: ",event.data);
      if (typeof event.data === 'string') {
        const message = JSON.parse(event.data);
        if (message.type === 'filename') {
          console.log("message",message);
          
          receivedFile.value = new Blob([], { type: 'application/octet-stream' });
          receivedFile.value.name = message.value;
        } else if (message.type === 'end') {
          console.log('文件传输完成');
        }
      } else {
        receivedFile.value = new Blob([receivedFile.value, event.data], { type: 'application/octet-stream' });
      }
    }; */

const receivedFileName = ref("");
let receivedFileChunks = []; // 用于存储接收到的文件数据
const fileDataChannelOnmessage = (event) => {
  console.log("fileDataChannelOnmessage: ", event.data);
  if (typeof event.data === 'string') {
    const message = JSON.parse(event.data);
    if (message.type === 'filename') {
      receivedFileChunks = []; // 清空数组以接收新的文件数据
      receivedFile.value = null; // 初始化接收文件对象
      receivedFileName.value = message.value; // 保存文件名

    } else if (message.type === 'end') {
      console.log('文件传输完成', receivedFileName.value);
      // 将文件数据转换为 Blob
      receivedFile.value = new Blob(receivedFileChunks, { type: 'application/octet-stream' });

      downloadFile()

    }
  } else {
    // 接收二进制数据并存储到数组中
    receivedFileChunks.push(event.data);
  }
};


// 下载文件
const downloadFile = () => {
  if (!receivedFile.value) return;

  // 触发下载
  const downloadLink = document.createElement('a');
  downloadLink.href = URL.createObjectURL(receivedFile.value);
  downloadLink.download = receivedFileName.value;
  downloadLink.click();

  // 释放 URL 对象
  URL.revokeObjectURL(downloadLink.href);
};

const createChatPeerConnection = (peerConnection) => {
  console.log("createChatPeerConnection", client.value);

  // 创建 DataChannel
  const dataChannel = peerConnection.createDataChannel('chat');
  if (!dataChannelList.value.has(client.value)) {
    dataChannelList.value.set(client.value, {
      chat: null,
      file: null
    });
  }
  dataChannelList.value.get(client.value).chat = dataChannel
  dataChannel.onopen = () => {
    console.log('ChatDataChannel 已打开');
  };


}

// 发送聊天信息
const sendChatMessage = (message) => {

  if (!dataChannelList.value.has(client.value)) {
    console.error('ChatDataChannel 未打开或不可用')
    return
  }

  const dataChannel = dataChannelList.value.get(client.value).chat;
  console.log("message", dataChannelList.value);
  console.log("message", dataChannel);

  if (dataChannel && dataChannel.readyState === 'open') {
    dataChannel.send(message);
    chatMessages.value.push({
      from: '自己',
      data: message
    });
  } else {
    console.error('ChatDataChannel 未打开或不可用');
  }
};

const createFilePeerConnection = (peerConnection) => {
  console.log("createFilePeerConnection", client.value);
  // 创建 DataChannel
  const dataChannel = peerConnection.createDataChannel('file');
  if (!dataChannelList.value.has(client.value)) {
    dataChannelList.value.set(client.value, {
      chat: null,
      file: null
    });
  }
  dataChannelList.value.get(client.value).file = dataChannel
  dataChannel.onopen = () => {
    console.log('FileDataChannel 已打开');
  };


}




// 发送offer
const sendOffer = async () => {
  client.value = clientList.value[userName.value].key

  if (!peerConnectionList.value.has(client.value)) {
    createPeerConnection();
  }

  console.log("发送offer");
  // 设置连接的用户
  const peerConnection = peerConnectionList.value.get(client.value);
  const offer = await peerConnection.createOffer();
  await peerConnection.setLocalDescription(offer);
  ws.send(JSON.stringify({
    msgtype: MessageType.signal,
    client: client.value,
    type: 'offer',
    data: offer
  }));
}

// 处理 offer
const handleOffer = async (message) => {
  console.log("处理offer", message)
  client.value = message.client

  if (!peerConnectionList.value.has(message.client)) {
    createPeerConnection();
  }

  console.log("handleOffer", peerConnectionList.value);

  const peerConnection = peerConnectionList.value.get(message.client);
  await peerConnection.setRemoteDescription(new RTCSessionDescription(message.data));

  const answer = await peerConnection.createAnswer();
  await peerConnection.setLocalDescription(answer);

  ws.send(JSON.stringify({
    msgtype: MessageType.signal,
    client: message.client,
    type: 'answer',
    data: answer
  }));
};

// 处理 answer
const handleAnswer = async (message) => {
  const peerConnection = peerConnectionList.value.get(client.value);
  await peerConnection.setRemoteDescription(new RTCSessionDescription(message.data));
};

// 处理 ICE candidate
const handleCandidate = async (message) => {
  try {
    const peerConnection = peerConnectionList.value.get(client.value);
    await peerConnection.addIceCandidate(new RTCIceCandidate(message.data));
  } catch (e) {
    console.error('添加 ICE Candidate 时出错:', e);
  }
};

// 登录
const login = () => {
  if (!userName.value) {
    alert('请输入用户名');
    return;
  }

  const loginMessage = {
    msgtype: MessageType.login,
    name: userName.value
  };
  ws.send(JSON.stringify(loginMessage));
  loggedIn.value = true;
};

// 断开点对点连接的方法
const disconnect = () => {
  const dataChannel = dataChannelList.value.get(client.value);
  if (dataChannel && dataChannel.readyState === 'open') {
    console.log(client.value, '断开点对点连接');
    dataChannel.close();
    dataChannelList.value.delete(client.value);
  }

  const peerConnection = peerConnectionList.value.get(client.value);
  if (peerConnection) {
    peerConnection.close();
    peerConnectionList.value.delete(client.value);
    console.log(client.value, '释放 peerConnection');
  }

  console.log("当前的连接peerConnection：", peerConnectionList.value);
  console.log("当前的连接dataChannel：", dataChannelList.value);

}

// 切换客户端
const switchClient = () => {
  client.value = clientList.value[userName.value].key
}


// 处理文件选择
const handleFileChange = (event) => {
  file.value = null;
  file.value = event.target.files[0];
};


// 发送文件
const sendFile2 = async () => {
  if (!file.value) return;

  const dataChannel = dataChannelList.value.get(client.value).file

  // 这里缓存不能调的太大，后期可以设置为配置文件的参数
  const chunkSize = 1024*32; // 每个chunk的大小
  const reader = new FileReader();

  reader.onload = (event) => {
    const arrayBuffer = event.target.result;
    const chunks = [];

    for (let i = 0; i < arrayBuffer.byteLength; i += chunkSize) {
      chunks.push(arrayBuffer.slice(i, i + chunkSize));
    }

    dataChannel.send(JSON.stringify({ type: 'filename', value: file.value.name }));
    /* chunks.forEach((chunk) => {

      const dataChannelBuf = () => {
        if (dataChannel.bufferedAmount >= chunkSize) {
          dataChannel.send(chunk);
        } else {
          // 设置延迟，防止发送太快，rtc缓存过满
          setTimeout(() => dataChannelBuf(), 100)
        }
      }

    }); */

    (async () => {
      for (const chunk of chunks) {
        while (dataChannel.bufferedAmount >= chunkSize) {
          await new Promise((resolve) => setTimeout(resolve, 100));
        }
        dataChannel.send(chunk);
      }

      dataChannel.send(JSON.stringify({ type: 'end' }));
      console.log('文件发送完成');
    })();

  };

  reader.readAsArrayBuffer(file.value);
};


const sendFile = async () => {
  if (!file.value) return;

  const dataChannel = dataChannelList.value.get(client.value).file;
  const chunkSize = 1024 * 64; // 增大 chunk 大小以提高速度
  const reader = new FileReader();

  reader.onload = async (event) => {
    const arrayBuffer = event.target.result;
    const chunks = [];

    for (let i = 0; i < arrayBuffer.byteLength; i += chunkSize) {
      chunks.push(arrayBuffer.slice(i, i + chunkSize));
    }

    dataChannel.send(JSON.stringify({ type: 'filename', value: file.value.name }));

    for (const chunk of chunks) {
      while (dataChannel.bufferedAmount >= chunkSize * 2) {
        // 仅在 buffer 过满时等待，减少不必要的延迟
        await new Promise((resolve) => setTimeout(resolve, 20));
      }
      dataChannel.send(chunk);
    }

    dataChannel.send(JSON.stringify({ type: 'end' }));
    console.log('文件发送完成');
  };

  reader.readAsArrayBuffer(file.value);
};



// 在组件挂载时连接 WebSocket
onMounted(() => {
  connectWebSocket();
  // console.log("window",window.location.host);
  
});

// 在组件卸载时关闭 WebSocket 连接
onUnmounted(() => {
  if (ws) {
    ws.close();
  }
});
</script>

<template>
  <div>
    <input v-model="userName" placeholder="输入用户名" />
    <button @click="login">登录</button>
    <button @click="sendChatMessage(userName)">发送聊天信息</button>
    <button @click="sendOffer">发送offer</button>
    <button @click="switchClient">切换client</button>
    <button @click="disconnect">断开点对点连接</button>

    <input type="file" @change="handleFileChange" />
    <button @click="sendFile">发送文件</button>
    <button v-if="receivedFile" @click="downloadFile">下载文件</button>

    <div v-if="loggedIn">
      <h3>在线用户列表:</h3>
      <ul>
        <li v-for="client in clientList" :key="client.key">{{ client.name }}</li>
      </ul>

      <h3>聊天信息:</h3>
      <ul>
        <li v-for="(msg, index) in chatMessages" :key="index">
          {{ msg.from }}: {{ msg.data }}
        </li>
      </ul>
    </div>
  </div>
</template>
